Skip to content

fix(capture): end a detached table cell before writing - #494

Merged
chhoumann merged 3 commits into
masterfrom
fix/stale-cell-capture
Oct 15, 2026
Merged

chhoumann merged 3 commits into
masterfrom
fix/stale-cell-capture

Conversation

@chhoumann

@chhoumann chhoumann commented Oct 15, 2026 •

Copy link
Copy Markdown
Owner

Fixes #473

Why

You edit a Live Preview table cell, then switch to Source mode or open another note in the tab. Obsidian keeps the cell's editor, but its table is gone from the view. If the note then changes elsewhere, a capture made before you click into the note rewrites the table from the cell's old copy at its old place. A row loses text, and a changed heading can delete a whole row with the capture. The same leftover cell can also swallow an Undo.

What changed

Scope

  • Covers captures and their Undo.
  • Leaves out Obsidian's caret after Back. If a change elsewhere adds lines above the caret while the tab shows another note, Back puts the caret on another line. A capture lands where typing would, which in a table can be the delimiter row. Master does the same for plain paragraphs, so it is a separate follow-up.

Tradeoffs

  • The brief asked the check to also require the cell to be on screen and its text to match the note. Attachment alone told stale cells from live ones in all 14 real-Obsidian scenarios I probed. A cell in a background tab is attached and hidden, and captures through it land correctly, so an on-screen check would close a live cell for nothing. A stale cell can still match the note's text at its old place when another row changed, so a text check trusts it and corrupts that row.
  • A fallback to the note's editor without ending the cell does not work. Obsidian hands a write inside the old cell's range to the stale cell, so the row is corrupted as on master.

Blast Radius

Only captures and Undos in a note whose tab holds a detached cell editor take the new path. If a future Obsidian drops destroyTableCell but keeps the cell, PodNotes keeps #471's behavior instead of throwing, and that can still corrupt the row. Obsidian 1.13.7 and 1.14.4 both have the method.

Verification

Mutations (standing order 32), each an exact replacement in CaptureService.ts:

Mutant Unit suite Table journeys, Obsidian 1.14.4
No liveness check (master) 2 fail 3 fail: Source mode, Back, Undo after another note's cell
End the cell before a capture only, not before Undo 1 fails 1 fails: Undo after another note's cell
Fall back to the note's editor without ending the cell 3 fail 2 fail: Back, Undo after another note's cell
Trust a cell whose text matches the note, not attachment 2 fail, mock only 2 fail: Source mode, Back
Require on screen (isShown) instead of attachment 2 fail, mock only 1 fails: background tab
Call destroyTableCell without the function check 2 fail 15 pass, same as the fix while Obsidian has the method

"Mock only" means the unit failures come from the mock, which has neither API.

An attachment-only mutant is this fix.

Docs at release

None. The table-cell line in timestamps.md stays accurate.

Before, master after another row changed elsewhere and Back. The other row lost text:

master, another row changed

After, the same steps on this branch:

fix, another row changed

Before, master after the heading changed elsewhere and Back. The table is gone:

master, heading changed

After, the same steps on this branch:

fix, heading changed


Note

Medium Risk
Changes capture/Undo write paths for notes with stale table-cell state; scope is narrow but can alter where text lands and how Undo restores notes in those edge cases.

Overview
Fixes #473 by tearing down stale Live Preview table cell editors before PodNotes writes a capture or runs capture Undo.

CaptureService now calls endDetachedTableCell before choosing the cell writer or reverting in the note editor. When Obsidian still holds a cell whose DOM node is disconnected (e.g. after Source mode or switching notes in the tab), it invokes destroyTableCell so the timestamp goes to the current note caret instead of rewriting an obsolete table range. Connected cells—including background-tab cells—still capture through the cell editor as before; Undo uses the same cleanup so a leftover cell cannot swallow revert.

Tests and the editInTableCell mock were extended for detached cells, missing destroyTableCell, and real-Obsidian journeys (Source/Back, background tab, Undo after another note’s stale cell).

Reviewed by Cursor Bugbot for commit cf8716d. Bugbot is set up for automated code reviews on this repo. Configure here.

Note

End detached table cells before capture writes and Undo restores

  • Fixes captures landing in stale table cells: CaptureService now calls a new endDetachedTableCell helper before selecting the writer, so a disconnected cell is destroyed and the capture reaches the current note editor (see CaptureService.ts).
  • The revertIn Undo helper ends a stale cell the same way before restoring the captured text.
  • The helper destroys a cell only when its container is disconnected and the editor component exposes a callable destruction method; connected cells are left unchanged.
  • Adds unit and end-to-end tests covering stale-cell capture, Undo, background-tab cells, and the fallback when destruction is unavailable (see table-cell-capture.test.ts).
  • Behavioral Change: captures that previously wrote into a detached cell's obsolete range now destroy that cell and write to the note editor instead; Undo restores the pre-capture note contents.

Macroscope summarized cf8716d.

Obsidian keeps a Live Preview table cell after its table leaves the
note's view, in Source mode or once the tab showed another note. A write
through the note then reaches that cell at its old offsets.

The journeys toggle Source mode or go Back after another note, change
another row on disk, capture, and Undo. On master the other row loses
text. A third journey captures, opens a note with a wide header cell in
the same tab, edits that cell, goes Back, and clicks Undo. On master the
Undo is lost.

The unit cases model a cell no longer in the document, once for a
capture and once for an Undo.
Source mode and opening another note in the tab replace the note's view
without ending its Live Preview table cell. That cell keeps the table's
old copy at its old place. A capture through it rewrote the table from
that copy, and Obsidian handed the note editor's writes in the cell's
old range to it, so a capture or an Undo could be lost or land in the
wrong place.

Before a capture or an Undo writes, a cell whose container is no longer
in the document is ended with Obsidian's destroyTableCell, as Obsidian
does when the note's editor takes focus. The capture then goes to the
note's caret, escaped as in Source mode. A cell still in the document
keeps taking captures, also in a background tab.
@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Oct 15, 2026 •

Copy link
Copy Markdown

Deploying podnotes with  Cloudflare Pages  Cloudflare Pages

Latest commit: cf8716d
Status: ✅  Deploy successful!
Preview URL: https://9a4f0985.podnotes.pages.dev
Branch Preview URL: https://fix-stale-cell-capture.podnotes.pages.dev

View logs

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

A capture can scramble a table row when a hidden cell editor is still attached

1 participant